/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sentry;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import io.sentry.protocol.SdkVersion;
import io.sentry.protocol.SentryId;
import io.sentry.protocol.SentryPackage;

import java.io.IOException;
import java.util.List;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * SentryEnvelopeHeaderAdapter
 *
 * @since 2021-06-02
 */
@ApiStatus.Internal
public final class SentryEnvelopeHeaderAdapter extends TypeAdapter<SentryEnvelopeHeader> {
    /**
     * name
     */
    public static final String NAME = "name";

    /**
     * version
     */
    public static final String VERSION = "version";

    @Override
    public void write(final @NotNull JsonWriter writer, final @Nullable SentryEnvelopeHeader value)
        throws IOException {
        if (value == null) {
            writer.nullValue();
            return;
        }
        writer.beginObject();
        if (value.getEventId() != null) {
            writer.name("event_id");
            writer.value(value.getEventId().toString());
        }
        final SdkVersion sdkVersion = value.getSdkVersion();
        if (sdkVersion != null) {
            if (hasValidSdkVersion(sdkVersion)) {
                writer.name("sdk").beginObject();
                writer.name(NAME).value(sdkVersion.getName());
                writer.name(VERSION).value(sdkVersion.getVersion());
                final List<String> integrations = sdkVersion.getIntegrations();
                if (integrations != null) {
                    writer.name("integrations").beginArray();
                    for (final String integration : integrations) {
                        writer.value(integration);
                    }
                    writer.endArray();
                }
                final List<SentryPackage> packages = sdkVersion.getPackages();
                if (packages != null) {
                    writer.name("packages").beginArray();
                    for (final SentryPackage item : packages) {
                        writer.beginObject();
                        writer.name(NAME).value(item.getName());
                        writer.name(VERSION).value(item.getVersion());
                        writer.endObject();
                    }
                    writer.endArray();
                }
                writer.endObject();
            }
        }
        writer.endObject();
    }

    /**
     * Returns if SdkVersion is a valid object, with non empty name and version
     *
     * @param sdkVersion the SdkVersion object
     * @return true if contains a valid name and version or false otherwise
     */
    private boolean hasValidSdkVersion(final @NotNull SdkVersion sdkVersion) {
        return sdkVersion.getName() != null
            && !sdkVersion.getName().isEmpty()
            && sdkVersion.getVersion() != null
            && !sdkVersion.getVersion().isEmpty();
    }

    @SuppressWarnings("deprecation")
    @Override
    public @Nullable SentryEnvelopeHeader read(final @NotNull JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }
        SentryId eventId = null;
        SdkVersion sdkVersion = null;
        reader.beginObject();
        while (reader.hasNext()) {
            switch (reader.nextName()) {
                case "event_id":
                    eventId = new SentryId(reader.nextString());
                    break;
                case "sdk": {
                    reader.beginObject();
                    sdkVersion = new SdkVersion();
                    while (reader.hasNext()) {
                        switch (reader.nextName()) {
                            case NAME:
                                sdkVersion.setName(reader.nextString());
                                break;
                            case VERSION:
                                sdkVersion.setVersion(reader.nextString());
                                break;
                            case "integrations":
                                reader.beginArray();
                                while (reader.hasNext()) {
                                    final String integration = reader.nextString();
                                    if (integration != null) {
                                        sdkVersion.addIntegration(integration);
                                    }
                                }
                                reader.endArray();
                                break;
                            case "packages":
                                // packages
                                reader.beginArray();
                                while (reader.hasNext()) {
                                    // packages item
                                    reader.beginObject();
                                    String name = null;
                                    String version = null;
                                    while (reader.hasNext()) {
                                        switch (reader.nextName()) {
                                            case NAME:
                                                name = reader.nextString();
                                                break;
                                            case VERSION:
                                                version = reader.nextString();
                                                break;
                                            default:
                                                reader.skipValue();
                                        }
                                    }
                                    if (name != null && version != null) {
                                        sdkVersion.addPackage(name, version);
                                    }
                                    reader.endObject();
                                }
                                reader.endArray();
                                break;
                            default:
                                reader.skipValue();
                                break;
                        }
                    }
                    reader.endObject();
                    break;
                }
                default:
                    reader.skipValue();
                    break;
            }
        }
        reader.endObject();
        return new SentryEnvelopeHeader(eventId, sdkVersion);
    }
}
